from zope.interface import implements

from twisted.internet import reactor, defer
from twisted.application import service
from twisted.plugin import getPlugins

import time, socket

from pyhnode import interfaces, errors, plugins, buffers, net, config

class PyHNodeService(service.Service):

    implements(interfaces.IParserService)

    def __init__(self):
        self.name = config.conf.get('general', 'nodename')
        self.conns = {}
        self.authed = []
        self.servers = {}
        for i in config.conf.get('general', 'servers').split(','):
            self.servers[i] = socket.gethostbyname(config.conf.get(i, 'addr'))
        self.splugins = {}
        for i in config.conf.get('plugins', 'simple').split(','):
            self.splugins[i] = object
        self.bplugins = {}
        for i in config.conf.get('plugins', 'buffered').split(','):
            self.bplugins[i] = {}
       
        p = getPlugins(interfaces.IPlugins, plugins)
        for i in p:
            i.configure()
            names = i.getNames()
            for j in names:
                if j in self.splugins.keys():
                    self.splugins[j] = i
                if j in self.bplugins.keys():
                    bt = config.conf.get(j, 'buftype')
                    if buffers.__all__.has_key(bt):
                        buff = buffers.__all__[bt](j)
                    self.bplugins[j] = [i, buff, config.conf.getint(j,\
                                        'period')]
        
        self.alert = []
        for i in getPlugins(interfaces.IAlertPlugin, net):
            i.configure()
            self.alert.append([i, 0])
        time.sleep(5)
        self.collect()

    def sendAlert(self, type, section, value):
        for i in self.alert:
            if i[1] == 0:
                i[0].sendAlert(self.name + " " + type + " " + section + " " +\
                               str(value))
                i[1] = 5
            else:
                i[1] -= 1
        
    def alertCheck(self, values, name):
        v = values[1]
        for i in v.keys():
            section = name + ":" + i
            if config.conf.has_section(section):
                if config.conf.has_option(section, "alert_min"):
                    if float(v[i]) < config.conf.getfloat(section,\
                        "alert_min"):
                        self.sendAlert("alert_min", section, v[i])
                if config.conf.has_option(section, "alert_max"):
                    if float(v[i]) > config.conf.getfloat(section,\
                        "alert_max"):
                        self.sendAlert("alert_max", section, v[i])
            if self.bplugins.has_key(name) and config.conf.get(name,\
                "alert_change") == "yes":
                if  len(self.bplugins[name][1].getlast()) > 0:
                    if self.bplugins[name][1].getlast()[0][1][i] != v[i]:
                        self.sendAlert("alert_change", section, v[i])
        return values
    
    def collect(self):
        for i in self.bplugins.keys():
            self.getpl(i, self.bplugins[i])

    def getpl(self, name, plug):
        if len(plug) != 0:
            d = plug[0].getValue(name)
            d.addErrback(errors.catchError)
            d.addCallback(self.alertCheck, name)
            d.addCallback(plug[1].append)
            reactor.callLater(plug[2], self.getpl, name, plug)

    def parseMsg(self, msg, p_inst):
        cmd, params = msg.split(' ')[0], msg.split(' ')[1:]
        if cmd == "error":
            print msg
        
        elif p_inst in self.authed:
            if cmd == "get" and len(params) == 1:
                if params[0] in self.splugins:
                    sep = "|"
                    d = self.splugins[params[0]].getValue(params[0])
                    def a(val):
                        ret = params[0] + " " + str(val[0]) + "|"
                        for i in val[1].keys():
                            ret += i + ":" + str(val[1][i]) + ','
                        return ret[:-1]
                    d.addCallback(self.alertCheck, params[0]) 
                    d.addCallback(a)
                    d.addErrback(errors.catchError)
                    return d

            elif cmd == "mget" and len(params) > 0 :
                if self.bplugins.has_key(params[0]):
                    sep, ret = ":", params[0] + " "
                    min = 0
                    if len(params) == 2 :
                        min = int(params[1])
                    for i in self.bplugins[params[0]][1].get():
                        if int(i[0]) > min:
                            ret += str(i[0]) + "|"
                            for j in i[1].keys():
                                ret += j + ":" + str(i[1][j]) + ","
                            ret = ret[:-1] + "#"
                    return defer.succeed(ret[:-1])

            elif cmd == "getsplugins" and len(params) == 0:
                sep = ","
                return defer.succeed("splugins " + \
                    sep.join(self.splugins.keys()))

            elif cmd == "getbplugins" and len(params) == 0:
                sep = ","
                return defer.succeed("bplugins " + \
                    sep.join(self.bplugins.keys()))

            elif cmd == "getconfig" and len(params) == 1:
                ret = "config " + params[0] + " "
                if params[0] in self.splugins.keys():
                    ret += "vtype:" + config.conf.get(params[0], 'value_type') + \
                        ";delay:" + config.conf.get(params[0], 'period')
                    ret += ";buffered:false" + \
                        self.splugins[params[0]].getConfig(params[0])
                elif params[0] in self.bplugins.keys():
                    ret += "vtype:" + config.conf.get(params[0], 'value_type') + \
                        ";delay:" + config.conf.get(params[0], 'period')
                    ret += ";buffered:true" + \
                        self.bplugins[params[0]][0].getConfig(params[0])
                else:
                    return "error " + msg
                return defer.succeed(ret)
            
            elif cmd == "set" and len(params) == 3:
                if params[0] in self.bplugins.keys() and params[1] == "period":
                    self.bplugins[params[0]][2] = int(params[2])
                    config.conf.set(params[0], params[1], params[2])
                elif params[0] in self.bplugins.keys() + self.splugins.keys():
                    section = params[0] + "." + params[1]
                    if not config.conf.has_section(section):
                        config.conf.add_section(section)
                    p = params[2].split(':')
                    if len(p) == 2:
                        config.conf.set(section, p[0], p[1])
                    if params[0] in self.bplugins.keys():
                        self.bplugins[params[0]][0].configure()
                    else:
                        self.splugins[params[0]].configure()
                else:
                    return defer.succeed("error:" + msg)
            
            elif cmd == "ok" and len(params) == 1:
                if self.bplugins.has_key(params[0]):
                    if config.conf.get(params[0], 'buftype') == 'linear':
                        self.bplugins[params[0]][1].clear()
            
            else:
                return defer.succeed("The '" + cmd + \
                    "' command is not implemented yet.")

        elif cmd == "hello" and len(params) == 1:
            if params[0] == self.name:
                return defer.succeed("you are?")
        elif cmd == "i'm" and len(params) == 1:
            if self.servers.has_key(params[0]):
                if self.servers[params[0]] == self.conns[p_inst]:
                    self.authed.append(p_inst)
                    def gettime(string):
                        return string + str(time.time())
                    return defer.succeed(gettime("hello, it's "))
        
        else:
            return defer.succeed("don't be rude, say hello")

    def getAnswer(self, msg, p_inst):
        d = self.parseMsg(msg, p_inst)
        if d != None:
            return d
        else:
            return defer.succeed(None)

    def newConnection(self, p_inst, ipaddr):
        """For state machines, maybe in the future..."""
        self.conns[p_inst]=ipaddr

    def closeConnection(self, p_inst):
        if p_inst in self.authed:
            self.authed.remove(p_inst)
        try:
            del self.conns[p_inst]
        except:
            pass

